package com.shurdroides.proyectofinal.server;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.channels.Channel;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

public class GaeChannel {

	//private static final Logger log = Logger.getLogger(Channel.class);
	
	public interface ChannelListener {

		void onOpen();

		void onMessage(String message);

		void onError(int code, String description);

		void onClose();

	}

	private enum ReadyState {
		CONNECTING, OPEN, CLOSING, CLOSED
	};

	private class XHR {

		private String mResponseText;

		private int mStatus;

		private String mStatusText;

		public XHR(HttpResponse response) throws IOException {
			StatusLine statusLine = response.getStatusLine();
			mStatus = statusLine.getStatusCode();
			mStatusText = statusLine.getReasonPhrase();
			HttpEntity httpEntity = response.getEntity();
			mResponseText = IOUtils.toString(httpEntity.getContent(), "UTF-8");
			// log.trace("RECV << " + StringUtils.chomp(mResponseText));
		}

		public String getStatusText() {
			return mStatusText;
		}

		public int getStatus() {
			return mStatus;
		}

		public boolean isSuccess() {
			return (mStatus == HttpStatus.SC_OK);
		}

		public String getResponseText() {
			return mResponseText;
		}

		public String toString() {
			return "xhr[status=" + mStatus + ",error=" + mStatusText
					+ ",message=" + mResponseText + "]";
		}
	}

	private ChannelListener defaultChannelListener = new ChannelListener() {

		@Override
		public void onOpen() {
			// log.warn("unhandled: onOpen");
		}

		@Override
		public void onMessage(String message) {
			// log.warn("unhandled: onMessage = " + message);
		}

		@Override
		public void onError(int code, String description) {
			// log.warn("unhandled: onError = " + description + " [" + code +
			// "]");
		}

		@Override
		public void onClose() {
			// log.warn("unhandled: onClose");
		}

	};

	private static final String BASE_URL = "https://apppruebalaferreria.appspot.com/_ah/channel/";

	private String mChannelId = null;

	private String mApplicationKey = null;

	private String mClientId = null;

	private ChannelListener mChannelListener = defaultChannelListener;

	private ReadyState mReadyState = ReadyState.CLOSED;

	private int POLLING_TIMEOUT_MS = 500;

	private HttpClient httpClient = new DefaultHttpClient();

	private Thread thPoll = null;

	public GaeChannel(String channelId, ChannelListener channelListener) {
		mChannelId = channelId;
		mApplicationKey = channelId.substring(channelId.lastIndexOf("-") + 1);
		mClientId = null;
		if (channelListener != null) {
			mChannelListener = channelListener;
		}
	}

	public void open() throws IOException {
		mReadyState = ReadyState.CONNECTING;
		connect(sendGet(getUrl("connect")));
	}

	public void close() throws IOException {
		mReadyState = ReadyState.CLOSING;
		disconnect(sendGet(getUrl("disconnect")));
	}

	private String getUrl(String command) throws IOException {
		String url = BASE_URL + "dev?command=" + command + "&channel=";
		url += URLEncoder.encode(mChannelId, "UTF-8");
		if (mClientId != null) {
			url += "&client=" + URLEncoder.encode(mClientId, "UTF-8");
		}
		return url;
	};

	private void connect(XHR xhr) {
		if (xhr.isSuccess()) {
			mClientId = xhr.getResponseText();
			mReadyState = ReadyState.OPEN;
			mChannelListener.onOpen();
			poll();
		} else {
			mReadyState = ReadyState.CLOSING;
			mChannelListener.onError(xhr.getStatus(), xhr.getStatusText());
			mReadyState = ReadyState.CLOSED;
			mChannelListener.onClose();
		}
	}

	private void disconnect(XHR xhr) {
		mReadyState = ReadyState.CLOSED;
		mChannelListener.onClose();
	}

	private void forwardMessage(XHR xhr) {
		if (xhr.isSuccess()) {
			String data = StringUtils.chomp(xhr.getResponseText());
			if (!StringUtils.isEmpty(data)) {
				mChannelListener.onMessage(data);
			}
			poll();
		} else {
			mChannelListener.onError(xhr.getStatus(), xhr.getStatusText());
		}
	}

	private void poll() {
		if (thPoll == null) {
			thPoll = new Thread(new Runnable() {

				@Override
				public void run() {
					XHR xhr = null;
					try {
						Thread.sleep(POLLING_TIMEOUT_MS);
						long tm = System.currentTimeMillis();
						xhr = sendGet(getUrl("poll"));
						thPoll = null;
						forwardMessage(xhr);
					} catch (Exception e) {
						// log.warn("Poll failed", e);
						thPoll = null;
					}
				}
			});
			thPoll.start();
		} else {
			// log.warn("Can't poll - still polling");
		}
	}

	private void beforeUnload() throws IOException {
		sendGet(getUrl("disconnect"));
	}

	private void forwardSendComplete(XHR xhr) {
		if (!xhr.isSuccess()) {
			mChannelListener.onError(xhr.getStatus(), xhr.getStatusText());
		}
	}

	private boolean send(String msg) throws IOException {
		if (mReadyState != ReadyState.OPEN) {
			return false;
		}
		String url = BASE_URL + "receive";
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		params.add(new BasicNameValuePair("key", mApplicationKey));
		params.add(new BasicNameValuePair("msg", msg));
		forwardSendComplete(sendPost(url, params));
		return true;
	}

	private XHR sendPost(String url, List<NameValuePair> params)
			throws IOException {
		UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "UTF-8");
		HttpPost httpPost = new HttpPost(url);
		httpPost.setEntity(entity);
		// log.trace("POST >> " + StringUtils.chomp(entity.toString()));
		return new XHR(httpClient.execute(httpPost));
	}

	private XHR sendGet(String url) throws IOException {
		HttpGet httpGet = new HttpGet(url);
		// log.trace("GET  >> " +
		// StringUtils.chomp(httpGet.getURI().getQuery()));
		return new XHR(httpClient.execute(httpGet));
	}

}
